package net.ys.mobileimsdk.server.bak;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eva.framework.utils.LoggerFactory;
import com.ys.chatserver.BaseConf;
import com.ys.chatserver.http.logic.LogicProcessor2;
import com.ys.chatserver.tool.IpAddressUtils;
import org.slf4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ChatBak {

    Logger logger = LoggerFactory.getLog();

    private final Timer bakTimer = new Timer();
    final DateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    final DateFormat yyyyMMddHHmm = new SimpleDateFormat("yyyyMMddHHmm");

    static String SEPERATE = IpAddressUtils.isWindowsOs() ? "\\\\" : "/";


    /**
     * 每隔1个小时执行一次备份
     */
    public void bakOutput() {
        final String ttl = BaseConf.getInstance().getFTP_BAK_GROUP_EXPIRE_TTL();
        bakTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                // -------------------数据备份------------------------
                String chatGroupBaseSql = "select g_id, g_status,g_name,g_owner_user_uid,g_notice,max_member_count,g_member_count,create_user_uid,create_time,forbid_user_uid,forbid_time,firbid_cause,g_notice_updatetime,g_notice_updateuid,del_user_uid,del_time,avatar_include_cnt from `chat_group_base` where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now()";
                String chatGroupNodeSql = "select user_uid, base_id,join_time,be_invite_user_id,be_owner_time,nickname_ingroup,msg_time_start,node_id,node_pid,node_type,node_rid,node_name,visible,c_ids,m_ids,s_ids,have_child,g_notice,g_status,max_member_count,g_member_count,g_notice_updatetime,g_notice_updateuid,create_time,g_owner_user_uid,create_user_uid,level,owner from `chat_group_node` where base_id in (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())";
                String chatMsgsCollectSql = "select collect_id,src_uid,dest_uid,chat_type,msg_type,msg_content,msg_time,online_count,msg_time2,fingerprint,parent_fp from chat_msgs_collect a left join (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())b on a.dest_uid like concat(b.g_id,'%')";
                String chatMsgsCollectArchivedSql = "select collect_id,src_uid,dest_uid,chat_type,msg_type,msg_content,msg_time,msg_time2,online_count,fingerprint,parent_fp from chat_msgs_collect_archived a left join (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())b on a.dest_uid like concat(b.g_id,'%')";
                String chatOfflineHistorySql = "select history_id,user_uid,friend_user_uid,history_type,history_content,history_content2,history_time2,history_time,chat_type,group_id,parent_fp from chat_offline_history a left join (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())b on a.group_id like concat(b.g_id,'%')";
                String gUserSql = "select user_uid,user_mail,nickname,user_psw,user_sex,register_ip,register_time,latest_login_time,latest_login_time2,latest_login_ip,user_status,is_online,user_avatar_file_name,what_s_up,user_desc,user_type,del_flag,account_type,dev_type,download_url,expire_time,creator_id,curr_node_id from g_users";
                String t_gps = "select id,gps_uid,gps_lat,gps_lng,group_id,create_time from t_gps where group_id in (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())";
                String tCloudSql = "select id,g_id,create_uid,create_time,update_time,file_name,file_type,file_size,file_md5,store_type from t_cloud where g_id in (select g_id from chat_group_base where DATE_ADD(create_time, INTERVAL " + ttl + " DAY) < now())";
                String missuOprLogSql = "select log_id,user_uid,opr_type,opr_desc,opr_content,opr_time,opr_bz from missu_oprlog ;";
                String structureInfoSql = "select id,structure_no,structure_name,structure_type,parent_no,root_no,structure_level,creator_id,creator_name,create_time,update_time,case_type,del_flag,is_finish,finish_time,structure_desc,gb28181_export from structure_info";
                String structureUserSql = "select id,structure_no,user_id,user_type,create_time,update_time,is_finish,del_flag,live_auth,file_download_auth,file_delete_auth,file_play_auth,setting_auth from structure_user_info";
                String chatBigFile = "select resource_id,res_type,res_file_name,res_human_size,res_size,res_md5,download_count,create_time,create_user_uid,update_time,update_user_id,file_status from chat_big_files";
                insertSqlFill(gUserSql, "g_users");
                Vector<Vector<?>> chatGroupBase = insertSqlFill(chatGroupBaseSql, "chat_group_base");
                insertSqlFill(chatGroupNodeSql, "chat_group_node");
                insertSqlFill(chatMsgsCollectSql, "chat_msgs_collect");
                insertSqlFill(chatMsgsCollectArchivedSql, "chat_msgs_collect_archived");
                insertSqlFill(tCloudSql, "t_cloud");
                insertSqlFill(structureInfoSql, "structure_info");
                insertSqlFill(structureUserSql, "structure_user_info");
                insertSqlFill(chatBigFile, "chat_big_files");
                new Thread(() -> {
                    insertSqlFill(chatOfflineHistorySql, "chat_offline_history");
                    insertSqlFill(t_gps, "t_gps");
                    insertSqlFill(missuOprLogSql, "missu_oprlog");
                }).start();
                // --------------------------文件备份---------------------------
                bakTask(chatGroupBase);
            }
        }, 20000, 3600000);
    }

    /**
     * 备份恢复
     */
    public void bakRecover() {
        final String ftpBakDir = BaseConf.getInstance().getFTP_BAK_DIR();
        bakTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                List<String> fileNames = FileUtil.listFileNames(ftpBakDir);
                fileNames.forEach(file -> {
                    // 数据恢复
                    if (file.endsWith(".sql")) {
                        List<String> list = Collections.emptyList();
                        if (file.equals("missu_oprlog.sql")) {
                            String sql = FileUtil.readString(ftpBakDir + file, StandardCharsets.UTF_8);
                            if (sql != null && !sql.isEmpty()) {
                                String[] split = sql.split(";");
                                if (split.length != 0) {
                                    list = Stream.of(split).collect(Collectors.toList());
                                }
                            }
                        } else {
                            list = FileUtil.readLines(ftpBakDir + file, StandardCharsets.UTF_8);
                        }

                        if (!list.isEmpty()) {
                            list.parallelStream().forEach(i -> {
                                try {
                                    LogicProcessor2.db.update(i, null, false);
                                } catch (Exception e) {
                                    logger.debug("跳过当前数据:{}", e.getMessage());
                                }
                            });
                        }
                    }
                    // 文件恢复
                    if (file.endsWith(".zip")) {
                        File uuidFile = new File(System.getProperty("user.dir") + File.separator + UUID.randomUUID());
                        File zipFile = new File(ftpBakDir + File.separator + file);
                        try {
                            uuidFile.mkdir();
                            ZipUtil.unzip(zipFile, uuidFile);
                            File[] files = uuidFile.listFiles();
                            if (Objects.nonNull(files)) {
                                Stream.of(files).forEach(z -> {
                                    String fileName = z.getName();
                                    String path = fileName.replaceAll("@@", SEPERATE);
                                    String filePath = path.replace("--", ":");
                                    File chatFile = new File(filePath);
                                    FileUtil.copy(z, chatFile, true);
                                });
                            }
                        } catch (Exception e) {
                            logger.error("压缩失败", e);
                        } finally {
                            FileUtil.del(uuidFile);
                        }
                    }
                });
            }
        }, 2000, 3600000);
    }

    /**
     * 备份服务
     */
    public void bakTask(Vector<Vector<?>> vectors) {
        try {
            String groupPath = BaseConf.getInstance().getFTP_BAK_DIR() + File.separator + "file";
            String groupZipPath = BaseConf.getInstance().getFTP_BAK_DIR() + "file.zip";
            File groupDataFile = new File(groupPath);
            File file = new File(groupZipPath);
            for (Vector<?> v : vectors) {
                String gId = v.get(0).toString();

                // 已经过期的或者被删除的群组,需要备份群组信息
                // 1.查询群组聊天信息
                // 普通聊天信息
                Vector<Vector<?>> msgsCollect = LogicProcessor2.db.queryData("select msg_type,msg_content,msg_time,src_uid,dest_uid from chat_msgs_collect where dest_uid like '" + gId + "%'");
                // 存档聊天信息
                Vector<Vector<?>> hisMsgsCollect = LogicProcessor2.db.queryData("select msg_type,msg_content,msg_time,src_uid,dest_uid from chat_msgs_collect_archived where dest_uid like '" + gId + "%'");

                writeAndCompress(msgsCollect, groupDataFile);

                writeAndCompress(hisMsgsCollect, groupDataFile);
            }
            try (FileOutputStream fos = new FileOutputStream(groupZipPath); ZipOutputStream zos = new ZipOutputStream(fos)) {
                if (!file.exists()) {
                    boolean res = file.createNewFile();
                }
                addDirToArchive(zos, groupDataFile, groupDataFile.getAbsolutePath());
            } catch (Exception e) {
                logger.error("压缩失败", e);
            } finally {
                FileUtil.del(groupDataFile);
            }
            // 4.删除群组聊天信息
//                boolean msgSucc = LogicProcessor2.db.update("delete from chat_msgs_collect where g_id is not null and g_id = " + gId, null, false);
//                boolean msgArchivedSucc = LogicProcessor2.db.update("delete from chat_msgs_collect_archived where g_id is not null and g_id = " + gId, null, false);
//                boolean msgHistorySucc = LogicProcessor2.db.update("delete from chat_offline_history where group_id is not null and group_id = " + gId, null, false);
//
//                // 5.删除群组聊天文件
//                FileUtil.del(groupPath);
//
//                // 6.删除群组信息
//                boolean update = LogicProcessor2.db.update("delete from chat_group_node where base_id = " + gId, null, false);
//
//                boolean succ = LogicProcessor2.db.update("delete from chat_group_base where g_id = " + gId, null, false);
        } catch (Exception e) {
            logger.error("-----------------bakTask exception---------------", e);
        }

    }

    private void writeAndCompress(Vector<Vector<?>> msgsCollect, File collectFile) throws Exception {
        {
            for (Vector<?> msg : msgsCollect) {

                int msgType = Integer.parseInt(msg.get(0).toString());
                String msgContent = msg.get(1).toString();
                switch (msgType) {
                    case 1:
                        // 图片
                        if (JSON.isValid(msgContent)) {
                            JSONObject jsonObject = JSON.parseObject(msgContent);
                            String fileName = jsonObject.getString("fileName");
                            String imageUrl = BaseConf.getInstance().getDIR_USER_IMAGE_UPLOAD_UNREAD() + fileName;
                            File imageFile = new File(imageUrl);
                            if (imageFile.exists()) {
                                StringBuffer buffer = new StringBuffer();
                                Stream.of(imageFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                    String replace = p.replace(":", "--");
                                    buffer.append(replace).append("@@");
                                });
                                buffer.append(fileName);
                                File file = new File(collectFile + File.separator + buffer);
                                FileUtil.copy(imageFile, file, true);
                                //FileUtil.del(imageFile);
                            } else {
                                String imageTimeoutUrl = BaseConf.getInstance().getDIR_USER_IMAGE_UPLOAD_TIMEOUT() + fileName;
                                File imageTimeoutFile = new File(imageTimeoutUrl);
                                if (imageTimeoutFile.exists()) {
                                    StringBuffer buffer = new StringBuffer();
                                    Stream.of(imageTimeoutFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                        String replace = p.replace(":", "--");
                                        buffer.append(replace).append("@@");
                                    });
                                    buffer.append(fileName);
                                    File file = new File(collectFile + File.separator + buffer);
                                    FileUtil.copy(imageTimeoutFile, file, true);
                                    //FileUtil.del(imageTimeoutFile);
                                } else {
                                    String imageReadFileUrl = BaseConf.getInstance().getDIR_USER_IMAGE_UPLOAD_READ() + fileName;
                                    File imageReadFile = new File(imageReadFileUrl);
                                    if (imageReadFile.exists()) {
                                        StringBuffer buffer = new StringBuffer();
                                        Stream.of(imageReadFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                            String replace = p.replace(":", "--");
                                            buffer.append(replace).append("@@");
                                        });
                                        buffer.append(fileName);
                                        File file = new File(collectFile + File.separator + buffer);
                                        FileUtil.copy(imageReadFile, file, true);
                                        //FileUtil.del(imageReadFile);
                                    }
                                }
                            }
                        } else {
                            logger.error("-------------图片类型信息json格式不正确---------");
                        }
                        break;
                    case 2:
                        // 语音
                        if (JSON.isValid(msgContent)) {
                            JSONObject jsonObject = JSON.parseObject(msgContent);
                            String fileName = jsonObject.getString("fileName");
                            String voiceUrl = BaseConf.getInstance().getDIR_USER_VOICE_UPLOAD_UNREAD() + fileName;
                            File voiceFile = new File(voiceUrl);
                            if (voiceFile.exists()) {
                                StringBuffer buffer = new StringBuffer();
                                Stream.of(voiceFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                    String replace = p.replace(":", "--");
                                    buffer.append(replace).append("@@");
                                });
                                buffer.append(fileName);
                                File file = new File(collectFile + File.separator + buffer);
                                FileUtil.copy(voiceFile, file, true);
                                //FileUtil.del(voiceFile);
                            } else {
                                String voiceTimeoutUrl = BaseConf.getInstance().getDIR_USER_VOICE_UPLOAD_TIMEOUT() + fileName;
                                File voiceTimeoutFile = new File(voiceTimeoutUrl);
                                if (voiceTimeoutFile.exists()) {
                                    StringBuffer buffer = new StringBuffer();
                                    Stream.of(voiceTimeoutFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                        String replace = p.replace(":", "--");
                                        buffer.append(replace).append("@@");
                                    });
                                    buffer.append(fileName);
                                    File file = new File(collectFile + File.separator + buffer);
                                    FileUtil.copy(voiceTimeoutFile, file, true);
                                    //FileUtil.del(voiceTimeoutFile);
                                } else {
                                    String voiceReadFileUrl = BaseConf.getInstance().getDIR_USER_VOICE_UPLOAD_READ() + fileName;
                                    File voiceReadFile = new File(voiceReadFileUrl);
                                    if (voiceReadFile.exists()) {
                                        StringBuffer buffer = new StringBuffer();
                                        Stream.of(voiceReadFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                            String replace = p.replace(":", "--");
                                            buffer.append(replace).append("@@");
                                        });
                                        buffer.append(fileName);
                                        File file = new File(collectFile + File.separator + buffer);
                                        FileUtil.copy(voiceReadFile, file, true);
                                        //FileUtil.del(voiceReadFile);
                                    }
                                }
                            }
                        } else {
                            logger.error("-------------语音类型信息json格式不正确---------");
                        }
                        break;
                    case 6:
                        // 视频
                        if (JSON.isValid(msgContent)) {
                            JSONObject jsonObject = JSON.parseObject(msgContent);
                            String fileName = jsonObject.getString("fileName");
                            String videoUrl = BaseConf.getInstance().getDIR_USER_SHORTVIDEO_UPLOAD() + fileName;
                            File videoFile = new File(videoUrl);
                            if (videoFile.exists()) {
                                StringBuffer buffer = new StringBuffer();
                                Stream.of(videoFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                    String replace = p.replace(":", "--");
                                    buffer.append(replace).append("@@");
                                });
                                buffer.append(fileName);
                                File file = new File(collectFile + File.separator + buffer);
                                FileUtil.copy(videoFile, file, true);
                                //FileUtil.del(videoFile);
                            } else {
                                String videoTimeoutUrl = BaseConf.getInstance().getDIR_USER_SHORTVIDEO_UPLOAD_TIMEOUT() + fileName;
                                File videoTimeoutFile = new File(videoTimeoutUrl);
                                if (videoTimeoutFile.exists()) {
                                    StringBuffer buffer = new StringBuffer();
                                    Stream.of(videoTimeoutFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                        String replace = p.replace(":", "--");
                                        buffer.append(replace).append("@@");
                                    });
                                    buffer.append(fileName);
                                    File file = new File(collectFile + File.separator + buffer);
                                    FileUtil.copy(videoTimeoutFile, file, true);
                                    //FileUtil.del(videoTimeoutFile);
                                } else {
                                    String videoFileUrl = BaseConf.getInstance().getDIR_USER_BIGFILE_UPLOAD() + fileName;
                                    File videoBigFile = new File(videoFileUrl);
                                    if (videoBigFile.exists()) {
                                        StringBuffer buffer = new StringBuffer();
                                        Stream.of(videoBigFile.getParentFile().getPath().split(SEPERATE)).forEach(p -> {
                                            String replace = p.replace(":", "--");
                                            buffer.append(replace).append("@@");
                                        });
                                        buffer.append(fileName);
                                        File file = new File(collectFile + File.separator + buffer);
                                        FileUtil.copy(videoBigFile, file, true);
                                        //FileUtil.del(videoBigFile);
                                    }
                                }
                            }
                        } else {
                            logger.error("-------------视频类型信息json格式不正确---------");
                        }
                        break;
                }
            }
        }
    }

    private void addDirToArchive(ZipOutputStream zos, File fileSource, String sourceDir) throws IOException {
        File[] files = fileSource.listFiles();
        if (Objects.nonNull(files)) {
            byte[] buffer = new byte[1024];
            int length;
            for (File file : files) {
                if (file.isDirectory()) {
                    addDirToArchive(zos, file, sourceDir);
                    continue;
                }
                FileInputStream fis = new FileInputStream(file);
                String entryName = file.getAbsolutePath().replace(sourceDir + File.separator, "").replace("\\", "/");
                ZipEntry ze = new ZipEntry(entryName);
                zos.putNextEntry(ze);
                while ((length = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, length);
                }
                fis.close();
            }
        }
    }

    private Vector<Vector<?>> insertSqlFill(String selectSql, String table) {
        try {
            final String ftpBakDir = BaseConf.getInstance().getFTP_BAK_DIR();
            final String baseGroupSqlFile = ftpBakDir + File.separator + table + ".sql";
            Vector<Vector<?>> schemas = LogicProcessor2.db.queryData("SELECT data_type FROM information_schema.columns WHERE table_name = '" + table + "';");
            Vector<Vector<?>> vectors = LogicProcessor2.db.queryData(selectSql);
            StringBuilder insertSql = new StringBuilder();
            vectors.forEach(v -> {
                insertSql.append("insert into `").append(table).append("` values (");
                for (int i = 0; i < v.size(); i++) {
                    Object value = v.get(i);
                    String schema = schemas.get(i).get(0).toString();
                    if ((schema.contains("char") || schema.contains("text") || schema.contains("varchar") || schema.contains("time") || schema.contains("enum")) && null != value && !"".equals(value)) {
                        insertSql.append("'").append(value).append("'");
                    } else if ("".equals(value)) {
                        insertSql.append("null");
                    } else {
                        insertSql.append(value);
                    }
                    if (i != v.size() - 1) {
                        insertSql.append(",");
                    }
                }
                insertSql.append(");\r\n");
            });

            FileUtil.appendUtf8String(insertSql.toString(), new File(baseGroupSqlFile));
            return vectors;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

}
